
# coding: utf-8

# ### 第一个深度学习作业，对数字图片进行识别，准确率要求达到98%以上

# ## 1、导包

# In[11]:


"""A very simple MNIST classifier.
See extensive documentation at
https://www.tensorflow.org/get_started/mnist/beginners
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import sys

from tensorflow.examples.tutorials.mnist import input_data

import tensorflow as tf

FLAGS = None


# ## 2、读取数据集

# In[12]:


data_dir = 'E:\\AI\\work\\deepLearn/input_data'
mnist = input_data.read_data_sets(data_dir,one_hot=True)


# ## 3、建立简单模型

# #### tesorflow中的激活函数
# 
# 所有激活函数 输入 和 输出 的维度是一样的
# tf.nn.relu()
# 
# tf.nn.sigmoid()
# 
# tf.nn.tanh()
# 
# tf.nn.elu()
# 
# tf.nn.bias_add()
# 
# tf.nn.crelu()
# 
# tf.nn.relu6()
# 
# tf.nn.softplus()
# 
# tf.nn.softsign()
# 
# tf.nn.dropout()
# 
# tf.nn.relu_layer(x, weights, biases,name=None)
# 
# def relu_layer(x, weights, biases, name=None):
# 
#   """Computes Relu(x * weight + biases).
#   Args:
#     x: a 2D tensor.  Dimensions typically: batch, in_units
#     weights: a 2D tensor.  Dimensions typically: in_units, out_units
#     biases: a 1D tensor.  Dimensions: out_units
#     name: A name for the operation (optional).  If not specified
#       "nn_relu_layer" is used.
#   Returns:
#     A 2-D Tensor computing relu(matmul(x, weights) + biases).
#     Dimensions typically: batch, out_units.
#   """

# In[13]:


#第一个版本
# x = tf.placeholder(tf.float32,shape=(None,784)) #定位符，行数不确定，784维特征
# W = tf.Variable(tf.zeros([784,10])) #权值 
# b = tf.Variable(tf.zeros([10]))  #偏置值
# y_pre = tf.placeholder(tf.float32,shape=(None,10)) #输出占位符，输出是10维（0-9）

# y = tf.matmul(x,W)+b #矩阵相乘(WX+B)
# # 改变激活函数relu
# #y = tf.nn.sigmoid(tf.matmul(x,W)+b)

#第二个版本添加隐层
INPUT_NODE = 784   # 输入层的节点数。对于 MNIST 数据集，就等于图片的像素
OUTPUT_NODE = 10  #输出层分类数


#先定义x,y占位符
x = tf.placeholder(tf.float32,shape=(None,INPUT_NODE)) #定位符，行数不确定，784维特征
y_pre = tf.placeholder(tf.float32,shape=(None,OUTPUT_NODE)) #输出占位符，输出是10维（0-9）

lr =tf.Variable(tf.constant(0.3),dtype=tf.float32) #学习率

BATCH_SIZE = 100  # 一个训练batch中的图片数
TRAINING_STEPS = 30000  # 训练轮数


# 添加隐层，在这里我们添加俩个隐层，因为超过俩个隐层就可以组合成任意图形

# In[14]:


"""这里添加隐层，第一层我们加入500个神经元，第二层我们加入300个神经元，尝试sigmoid 和relu俩种激活函数
    在这里没有使用tf.zero函数，而是采用tf.truncated_normal随机生成数的方式来初始化参数
"""
W1 = tf.Variable(tf.truncated_normal([784, 500], stddev=0.1), name='W1')
b1 = tf.Variable(tf.zeros([500]) + 0.1, name='b1')
L1 = tf.nn.relu(tf.matmul(x, W1) + b1, name='L1') #第一个隐层

W2 = tf.Variable(tf.truncated_normal([500, 300], stddev=0.1), name='W2')
b2 = tf.Variable(tf.zeros([300]) + 0.1, name='b2')
L2 = tf.nn.relu(tf.matmul(L1, W2) + b2, name='L2') #第二个隐层

#输出层权值
W3 = tf.Variable(tf.truncated_normal([300, 10], stddev=0.1), name='W3')
b3 = tf.Variable(tf.zeros([10]) + 0.1, name='b3')




# ### 定义正则化参数

# In[15]:


#使用tf自带的正则,这里采用正则，更加平滑
regularizer = tf.contrib.layers.l2_regularizer(0.0001) #采用l2正则让模型更平滑
regularizertion = regularizer(W1) + regularizer(W2) # 计算正则化损失函数的值


# ## 4、定义损失函数，这里使用交叉熵函数

# In[16]:


y = tf.matmul(L2,W3)+b3
#cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_pre,logits=y)) #取平均值
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_pre,logits=y))+regularizertion #加上损失l2正则损失


# ## 5.定义训练目标

# In[17]:


#生成一个训练step
train_step = tf.train.GradientDescentOptimizer(learning_rate=lr).minimize(cross_entropy) #随机梯度下降算法 

# sion = tf.Session() #定义session
# init_op = tf.global_variables_initializer()
# sion.run(init_op)


# #### 经过调整学习率经过选择选择0.3最为合适

# #### 定义训练目标，并定义session,以及初始化所有变量

# #### 在这里我们仍然调用系统提供的读取数据，为我们取得一个batch。 然后我们运行3k个step(5 epochs)，对权重进行优化。

# In[18]:


# for _ in range(3000):
#     batch_xs,batch_ys = mnist.train.next_batch(100) #遍历3000次每次处理100个点
#     sion.run(train_step,feed_dict={x:batch_xs,
#                                    y_pre:batch_ys
#         }) #执行训练操作，第一个是训练函数，第二个是数据字典


# #### 验证我们模型在测试数据上的准确率

# In[19]:


# corre_pre = tf.equal(tf.argmax(y,1),tf.argmax(y_pre,1)) #计算概率，第一个是预测值，第二个是真实值
# accuracy = tf.reduce_mean(tf.cast(corre_pre,tf.float32))
# print(sion.run(accuracy,feed_dict={x: mnist.test.images,
#                                   y_pre: mnist.test.labels}))


# ### 迭代输出结果

# In[23]:


init_op = tf.global_variables_initializer()
corre_pre = tf.equal(tf.argmax(y,1),tf.argmax(y_pre,1)) #计算概率，第一个是预测值，第二个是真实值
accuracy = tf.reduce_mean(tf.cast(corre_pre,tf.float32))

with tf.Session() as sion:
    sion.run(init_op)
    #循环多次输出
    validate_feed = {x: mnist.validation.images, y_pre: mnist.validation.labels}
    # 准备测试数据。在真实的应用中，这部分数据在训练时是不可见的，这个数据只是作为模型优劣的最后评价标准
    test_feed = {x: mnist.test.images, y_pre: mnist.test.labels}

    for _ in range(TRAINING_STEPS):
         # 每1000轮输出一次在验证数据集上的测试结果
        if _ % 1000 == 0:
            validate_acc = sion.run(accuracy, feed_dict=validate_feed)
            print("After %d training step(s), validation accuracy using average model is %g" % (_, validate_acc))

        batch_xs,batch_ys = mnist.train.next_batch(BATCH_SIZE) #遍历3000次每次处理100个点
        sion.run(train_step,feed_dict={x:batch_xs,
                               y_pre:batch_ys
        }) #执行训练操作，第一个是训练函数，第二个是数据字典

    test_acc = sion.run(accuracy,feed_dict=test_feed)
    print("After %d training step(s), test accuracy using average model is %g" % (TRAINING_STEPS, test_acc))


    


# 经过上面的数据我们可以发现7000以后数据就开始出现波动值，一直在这附近波动，说明到了谷底了，迭代也就结束了
